package com.bailian.app.service;

import com.bailian.app.exception.LoginException;
import com.bailian.app.domain.WechatLoginUser;
import com.bailian.common.utils.StringUtils;
import com.bailian.common.core.redis.RedisCache;
import com.bailian.common.core.domain.model.LoginUser;
import com.bailian.system.domain.BlPhoneUser;
import com.bailian.common.core.domain.entity.SysUser;
import com.bailian.system.service.IBlPhoneUserService;
import com.bailian.framework.web.service.JwtTokenService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 微信小程序服务类
 * 
 * @author bailian
 */
@Service
public class WechatMiniProgramService {

    private static final Logger logger = LoggerFactory.getLogger(WechatMiniProgramService.class);

    @Autowired
    private IBlPhoneUserService phoneUserService;
    
    @Autowired
    private RestTemplate restTemplate;
    
    @Autowired
    private RedisCache redisCache;
    
    @Autowired
    private JwtTokenService jwtTokenService;
    
    @Value("${wechat.miniprogram.appid}")
    private String appId;
    
    @Value("${wechat.miniprogram.secret}")
    private String secret;
    
    @Value("${token.expireTime:30}")
    private int expireTime;
    
    private static final String WECHAT_API_URL = "https://api.weixin.qq.com/sns/jscode2session";
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final String WECHAT_TOKEN_PREFIX = "WECHATTOKEN";

    /**
     * 微信小程序登录
     * @param code 微信授权码
     * @param nickName 昵称
     * @param avatarUrl 头像
     * @return 登录结果
     */
    public Map<String, Object> wxLogin(String code, String nickName, String avatarUrl,String encryptedData,String iv) {
        logger.info("开始微信登录，code: {}, nickName: {}", code, nickName);
        
        try {
            // 参数验证
            if (StringUtils.isEmpty(code)) {
                throw new LoginException("INVALID_CODE", "微信授权码不能为空");
            }
            
            // 1. 调用微信接口获取openid和session_key
            Map<String, String> wxResult = callWechatApi(code);
            
            String openid = wxResult.get("openid");
            String sessionKey = wxResult.get("session_key");
            String unionid = wxResult.get("unionid");
            if (StringUtils.isEmpty(openid)) {
                logger.error("微信API返回的openid为空");
                throw new LoginException("INVALID_OPENID", "获取微信用户信息失败");
            }
            
            logger.info("微信API调用成功，openid: {}", openid);
            
            // 2. 查询或创建用户
            BlPhoneUser user = findOrCreateUser(openid, nickName, avatarUrl, unionid);
            
            // 3. 创建微信登录用户对象并生成JWT token
            WechatLoginUser loginUser = createWechatLoginUser(user, openid);
            String jwtToken = generateJwtToken(loginUser);
            
            // 4. 缓存登录用户信息（用于后续验证）
            cacheLoginUser(loginUser);
            
            // 5. 返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("token", jwtToken);
            result.put("openid", openid);
            result.put("sessionKey", sessionKey);
            result.put("userInfo", convertToUserInfo(user));
            result.put("tokenType", "JWT");
            
            logger.info("微信登录成功，userId: {}, openid: {}", user.getUserId(), openid);
            return result;
            
        } catch (LoginException e) {
            logger.error("微信登录失败: {}", e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            logger.error("微信登录失败: {}", e.getMessage(), e);
            throw new LoginException("LOGIN_ERROR", "微信登录失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 调用微信API
     */
    private Map<String, String> callWechatApi(String code) throws Exception {
        String url = String.format("%s?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
                WECHAT_API_URL, appId, secret, code);
        
        logger.debug("调用微信API，URL: {}", url);
        
        String response = restTemplate.getForObject(url, String.class);
        logger.debug("微信API响应: {}", response);
        
        JsonNode jsonNode = objectMapper.readTree(response);
        
        Map<String, String> result = new HashMap<>();
        if (jsonNode.has("errcode") && jsonNode.get("errcode").asInt() != 0) {
            String errorMsg = jsonNode.get("errmsg").asText();
            logger.error("微信API错误: errcode={}, errmsg={}", jsonNode.get("errcode").asInt(), errorMsg);
            throw new RuntimeException("微信API错误: " + errorMsg);
        }
        
        result.put("openid", jsonNode.get("openid").asText());
        result.put("session_key", jsonNode.get("session_key").asText());
        if (jsonNode.has("unionid")) {
            result.put("unionid", jsonNode.get("unionid").asText());
        }
        
        return result;
    }
    
    /**
     * 查找或创建用户
     */
    private BlPhoneUser findOrCreateUser(String openid, String nickName, String avatarUrl, String unionid) {
        logger.debug("查找或创建用户，openid: {}, nickName: {}", openid, nickName);
        
        // 根据openid查找用户
        BlPhoneUser queryUser = new BlPhoneUser();
        queryUser.setWxToken(openid);
        List<BlPhoneUser> existUsers = phoneUserService.selectBlPhoneUserList(queryUser);
        
        BlPhoneUser user;
        if (existUsers != null && !existUsers.isEmpty()) {
            // 用户已存在，更新信息
            user = existUsers.get(0);
            logger.info("用户已存在，更新信息，userId: {}", user.getUserId());
            
            user.setNikeName(nickName);
            user.setHeadPicture(avatarUrl);
            phoneUserService.updateBlPhoneUser(user);
        } else {
            // 创建新用户
            logger.info("创建新用户，openid: {}", openid);
            
            user = new BlPhoneUser();
            user.setNikeName(nickName);
            user.setHeadPicture(avatarUrl);
            user.setWxToken(openid);
            user.setSource("微信小程序");
            phoneUserService.insertBlPhoneUser(user);
        }
        
        return user;
    }
    
    /**
     * 解析微信手机号
     * @param encryptedData 加密数据
     * @param iv 初始向量
     * @param sessionKey 会话密钥
     * @return 手机号
     */
    public String decryptPhoneNumber(String encryptedData, String iv, String sessionKey) {
        logger.info("开始解密手机号");
        
        try {
            // 参数验证
            if (StringUtils.isEmpty(encryptedData) || StringUtils.isEmpty(iv) || StringUtils.isEmpty(sessionKey)) {
                throw new RuntimeException("解密参数不能为空");
            }
            
            // 使用AES解密
            byte[] keyBytes = Base64.getDecoder().decode(sessionKey);
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
            
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            String decryptedData = new String(decryptedBytes, StandardCharsets.UTF_8);
            
            logger.debug("解密后的数据: {}", decryptedData);
            
            // 解析JSON获取手机号
            JsonNode jsonNode = objectMapper.readTree(decryptedData);
            String phoneNumber = jsonNode.get("phoneNumber").asText();
            
            logger.info("手机号解密成功: {}", phoneNumber);
            return phoneNumber;
            
        } catch (Exception e) {
            logger.error("解密手机号失败: {}", e.getMessage(), e);
            throw new RuntimeException("解密手机号失败: " + e.getMessage());
        }
    }
    
    /**
     * 绑定手机号
     */
    public BlPhoneUser bindPhoneNumber(Long userId, String phoneNumber) {
        BlPhoneUser user = phoneUserService.selectBlPhoneUserById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        
        // 检查手机号是否已被其他用户绑定
        BlPhoneUser queryUser = new BlPhoneUser();
        queryUser.setPhone(phoneNumber);
        List<BlPhoneUser> existUsers = phoneUserService.selectBlPhoneUserList(queryUser);
        if (existUsers != null && !existUsers.isEmpty()) {
            for (BlPhoneUser existUser : existUsers) {
                if (!existUser.getUserId().equals(userId)) {
                    throw new RuntimeException("该手机号已被其他用户绑定");
                }
            }
        }
        
        user.setPhone(phoneNumber);
        phoneUserService.updateBlPhoneUser(user);
        return user;
    }
    
    /**
     * 创建微信登录用户对象
     * @param user 手机用户信息
     * @param openid 微信openid
     * @return 微信登录用户对象
     */
    private WechatLoginUser createWechatLoginUser(BlPhoneUser user, String openid) {
        WechatLoginUser loginUser = new WechatLoginUser(user, openid);
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * 60 * 1000L);
        return loginUser;
    }
    
    /**
     * 生成JWT token
     * @param loginUser 微信登录用户对象
     * @return JWT token
     */
    private String generateJwtToken(WechatLoginUser loginUser) {
        try {
            // 将WechatLoginUser转换为LoginUser，然后生成JWT token
            LoginUser convertedLoginUser = convertToLoginUser(loginUser);
            String jwtToken = jwtTokenService.generateTokenWithRoles(convertedLoginUser, false);
            
            // 同时生成一个内部token用于缓存
            String internalToken = java.util.UUID.randomUUID().toString().replaceAll("-", "");
            loginUser.setToken(internalToken);
            
            logger.debug("生成微信小程序JWT token成功，userId: {}, internalToken: {}", loginUser.getUserId(), internalToken);
            return jwtToken;
            
        } catch (Exception e) {
            logger.error("生成微信小程序JWT token失败: {}", e.getMessage(), e);
            throw new RuntimeException("生成微信小程序JWT token失败");
        }
    }
    
    /**
     * 将WechatLoginUser转换为LoginUser
     * @param wechatLoginUser 微信登录用户
     * @return 转换后的LoginUser
     */
    private LoginUser convertToLoginUser(WechatLoginUser wechatLoginUser) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(wechatLoginUser.getUserId());
        loginUser.setToken(wechatLoginUser.getToken());
        loginUser.setLoginTime(wechatLoginUser.getLoginTime());
        loginUser.setExpireTime(wechatLoginUser.getExpireTime());
        loginUser.setIpaddr(wechatLoginUser.getIpaddr());
        loginUser.setLoginLocation(wechatLoginUser.getLoginLocation());
        loginUser.setBrowser(wechatLoginUser.getBrowser());
        loginUser.setOs(wechatLoginUser.getOs());
        loginUser.setPermissions(wechatLoginUser.getPermissions());
        
        // 创建一个临时的SysUser对象，避免空指针异常
        // 这里我们创建一个基本的用户信息，用于JWT token生成
        SysUser sysUser = new SysUser();
        sysUser.setUserId(wechatLoginUser.getUserId());
        // 从BlPhoneUser中获取用户名，如果没有则使用默认值
        if (wechatLoginUser.getUser() != null) {
            sysUser.setUserName(wechatLoginUser.getUser().getNikeName());
            sysUser.setNickName(wechatLoginUser.getUser().getNikeName());
        } else {
            sysUser.setUserName("wechat_user_" + wechatLoginUser.getUserId());
            sysUser.setNickName("微信用户");
        }
        sysUser.setStatus("0"); // 正常状态
        
        loginUser.setUser(sysUser);
        
        return loginUser;
    }
    
    /**
     * 生成内部token（用于缓存）
     * @param loginUser 微信登录用户对象
     * @return 内部token
     */
    private String generateInternalToken(WechatLoginUser loginUser) {
        try {
            // 生成不包含下划线和横线的token
            String token = java.util.UUID.randomUUID().toString().replaceAll("-", "");
            loginUser.setToken(token);
            
            logger.debug("生成微信小程序内部token成功，userId: {}, token: {}", loginUser.getUserId(), token);
            return token;
            
        } catch (Exception e) {
            logger.error("生成微信小程序内部token失败: {}", e.getMessage(), e);
            throw new RuntimeException("生成微信小程序内部token失败");
        }
    }
    
    /**
     * 缓存登录用户信息（参考TokenService的实现方式）
     * @param loginUser 微信登录用户对象
     */
    private void cacheLoginUser(WechatLoginUser loginUser) {
        try {
            String userKey = getWechatTokenKey(loginUser.getToken());
            redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
            logger.debug("缓存微信小程序登录用户信息成功，userId: {}, token: {}", loginUser.getUserId(), loginUser.getToken());
        } catch (Exception e) {
            logger.error("缓存微信小程序登录用户信息失败: {}", e.getMessage(), e);
            throw new RuntimeException("缓存微信小程序登录用户信息失败");
        }
    }
    
    /**
     * 获取微信小程序token的缓存key
     * @param token token
     * @return 缓存key
     */
    private String getWechatTokenKey(String token) {
        return "wechat_login_token:" + token;
    }
    
    /**
     * 验证token
     * @param token token
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        if (token == null) {
            return false;
        }
        
        try {
            // 如果是JWT token，使用JwtTokenService验证
            if (jwtTokenService.validateToken(token)) {
                return true;
            }
            
            // 如果是旧的内部token格式，兼容性验证
            if (token.startsWith(WECHAT_TOKEN_PREFIX)) {
                String actualToken = token.substring(WECHAT_TOKEN_PREFIX.length());
                String userKey = getWechatTokenKey(actualToken);
                WechatLoginUser loginUser = redisCache.getCacheObject(userKey);
                return loginUser != null && loginUser.getExpireTime() > System.currentTimeMillis();
            }
            
            return false;
        } catch (Exception e) {
            logger.error("验证微信小程序token失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 从token中获取用户ID
     * @param token token
     * @return 用户ID
     */
    public Long getUserIdFromToken(String token) {
        if (token == null) {
            return null;
        }
        
        try {
            // 如果是JWT token，使用JwtTokenService获取用户ID
            Long userId = jwtTokenService.getUserIdFromToken(token);
            if (userId != null) {
                return userId;
            }
            
            // 如果是旧的内部token格式，兼容性获取
            if (token.startsWith(WECHAT_TOKEN_PREFIX)) {
                String actualToken = token.substring(WECHAT_TOKEN_PREFIX.length());
                String userKey = getWechatTokenKey(actualToken);
                WechatLoginUser loginUser = redisCache.getCacheObject(userKey);
                
                if (loginUser != null && loginUser.getExpireTime() > System.currentTimeMillis()) {
                    return loginUser.getUserId();
                }
            }
            
            return null;
        } catch (Exception e) {
            logger.error("从微信小程序token获取用户ID失败: {}", e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 刷新token有效期
     * @param token token
     * @return 是否刷新成功
     */
    public boolean refreshToken(String token) {
        if (token == null) {
            return false;
        }
        
        try {
            // 如果是JWT token，使用JwtTokenService刷新
            String newJwtToken = jwtTokenService.refreshToken(token);
            if (newJwtToken != null && !newJwtToken.isEmpty()) {
                logger.debug("刷新微信小程序JWT token成功");
                return true;
            }
            
            // 如果是旧的内部token格式，兼容性刷新
            if (token.startsWith(WECHAT_TOKEN_PREFIX)) {
                String actualToken = token.substring(WECHAT_TOKEN_PREFIX.length());
                String userKey = getWechatTokenKey(actualToken);
                WechatLoginUser loginUser = redisCache.getCacheObject(userKey);
                
                if (loginUser != null) {
                    loginUser.setExpireTime(System.currentTimeMillis() + expireTime * 60 * 1000L);
                    redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
                    logger.debug("刷新微信小程序内部token成功，userId: {}", loginUser.getUserId());
                    return true;
                }
            }
            
            return false;
        } catch (Exception e) {
            logger.error("刷新微信小程序token失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 删除登录用户信息
     * @param token token
     */
    public void delLoginUser(String token) {
        if (token == null || !token.startsWith(WECHAT_TOKEN_PREFIX)) {
            return;
        }
        
        try {
            String actualToken = token.substring(WECHAT_TOKEN_PREFIX.length());
            String userKey = getWechatTokenKey(actualToken);
            redisCache.deleteObject(userKey);
            logger.debug("删除微信小程序登录用户信息成功，token: {}", token);
        } catch (Exception e) {
            logger.error("删除微信小程序登录用户信息失败: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 转换用户信息
     */
    private Map<String, Object> convertToUserInfo(BlPhoneUser user) {
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("userId", user.getUserId());
        userInfo.put("nickName", user.getNikeName());
        userInfo.put("avatarUrl", user.getHeadPicture());
        userInfo.put("phone", user.getPhone());
        userInfo.put("name", user.getName());
        userInfo.put("source", user.getSource());
        return userInfo;
    }
} 